package com.asggo.g21.utils;

import cn.hutool.core.net.Ipv4Util;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.util.ResourceUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * ip 工具.
 *
 * @author eric 2024/2/27 11:16
 */
@Slf4j
public class IpUtils {

  private static Searcher searcher = null;

  static {
    // 1、从 dbPath 加载整个 xdb 到内存。
    byte[] cBuff = new byte[20971520];
    String dbPath = "classpath:ip2region/ip2region.xdb";
    try {
      final File file = ResourceUtils.getFile(dbPath);
      cBuff = Searcher.loadContentFromFile(file.getPath());
    } catch (Exception e) {
      log.error("failed to load content from {}: {}", dbPath, ExceptionUtils.getStackTrace(e));
    }

    // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。

    try {
      searcher = Searcher.newWithBuffer(cBuff);
    } catch (Exception e) {
      log.error("failed to create content cached searcher: {}", ExceptionUtils.getStackTrace(e));
    }
  }

  private IpUtils() {
    //
  }

  private static final String[] IP_HEADER_CANDIDATES = {
      "X-Real-IP",
      "X-Forwarded-For",
      "Proxy-Client-IP",
      "WL-Proxy-Client-IP",
      "HTTP_X_FORWARDED_FOR",
      "HTTP_X_FORWARDED",
      "HTTP_X_CLUSTER_CLIENT_IP",
      "HTTP_CLIENT_IP",
      "HTTP_FORWARDED_FOR",
      "HTTP_FORWARDED",
      "HTTP_VIA",
      "REMOTE_ADDR"
  };

  public static String getClientIpAddressIfServletRequestExist() {
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    if (requestAttributes == null) {
      return "0.0.0.0";
    }

    HttpServletRequest request = ((ServletRequestAttributes)
        requestAttributes).getRequest();
    for (String header : IP_HEADER_CANDIDATES) {
      String ipList = request.getHeader(header);
      if (ipList != null && !ipList.isEmpty() && !"unknown".equalsIgnoreCase(ipList)) {
        return ipList.split(",")[0];
      }
    }

    return request.getRemoteAddr();
  }

  public static String getRegion(final String ip) {
    if (Ipv4Util.isInnerIP(ip)) {
      return "内网ip";
    }

    String region = ip;
    // 3、查询
    try {
      region = searcher.search(ip);
      if (region == null) {
        Map<String, Object> ipParamMap = new LinkedHashMap<>();
        ipParamMap.put("key", "C92BAA7E03099C0B67D9D38C4AF5B708");
        ipParamMap.put("ip", ip);
        final String ipRegion = HttpUtil.get("https://api.ip2location.io", ipParamMap);
        final JSONObject ipJson = JSONUtil.parseObj(ipRegion);
        final String latitude = ipJson.getStr("latitude");
        final String longitude = ipJson.getStr("longitude");
        final String zipCode = ipJson.getStr("zip_code");
        log.info("latitude {}, longitude {}, zipCode {}", latitude, longitude, zipCode);

        Map<String, Object> paramMap = new LinkedHashMap<>();
        paramMap.put("key", "8780449cddb680e5e7175d77cdcde17b");
        paramMap.put("location", longitude + "," + latitude);
        paramMap.put("extensions", "base");
        paramMap.put("roadlevel", 0);
        final String ipLocation = HttpUtil.get("https://restapi.amap.com/v3/geocode/regeo",
            paramMap);
        region = JSONUtil.parseObj(ipLocation).getJSONObject("regeocode")
            .getStr("formatted_address");
      }
      log.info("{region: {}, ioCount: {}\n", region, searcher.getIOCount());
    } catch (Exception e) {
      log.error("failed to search({}): {}\n", ip, ExceptionUtils.getStackTrace(e));
    }

    return region;
  }
}
